package filescript;
import filters.InvalidFilterParameterException;
import filters.UnsupportedFilterException;

public class FilterParser {
	
	private static final int COMPOUND_MARKER = 2;
	private static final int BASIC_MARKER = 1;
	private static final int NOT_FOUND = -1;
	private static final int FIRST_INDEX_OF_STRING = 0;
	
	private static final String LEFT_PARENTHESES = "(";
	private static final String RIGHT_PARENTHESES = ")";
	private static final String NOT_OPERATOR = "NOT";
	private static final String SPACE = " ";
	
	
	
	
	/**
	 * Build an expression tree from a given filter
	 * @param filter The string to process
	 * @return An ExpressionTreeNode tree head
	 * @throws InvalidFilterParameterException 
	 * @throws UnsupportedFilterException 
	 * @throws InvalidFilterExpression 
	 * @throws InvalidFilterExpression
	 * @throws UnsupportedFilterException
	 * @throws InvalidFilterParameterException
	 */
	protected static ExpressionTreeNode parseExpression(String filter) throws InvalidFilterExpression, UnsupportedFilterException, InvalidFilterParameterException {
		return parseExpression(null, filter);
	}
	
	/**
	 * 
	 * @param node
	 * @param filter
	 * @return
	 * @throws InvalidFilterExpression
	 * @throws UnsupportedFilterException
	 * @throws InvalidFilterParameterException
	 */
	private static ExpressionTreeNode parseExpression(ExpressionTreeNode node, String filter) throws InvalidFilterExpression, UnsupportedFilterException, InvalidFilterParameterException {
		if(detectFilterType(filter)==BASIC_MARKER) {
			if(filter.contains(SPACE)) { //If a basic filter contains a space, too many arguments were provided
				throw new InvalidFilterExpression();
			}
			return new ExpressionTreeNode(filter);
		} 
		else { // This is a compound filter
			String notString = removeParentheses(filter);
			if(notString.startsWith(NOT_OPERATOR.concat(SPACE))) {
				if(notString.indexOf(RIGHT_PARENTHESES)!=NOT_FOUND) {
					return new ExpressionTreeNode(null,parseExpression(node,filter.substring(filter.indexOf(SPACE)+1, findClosingParentheses(filter))),NOT_OPERATOR);
				} else {
					return new ExpressionTreeNode(null,parseExpression(node,filter.substring(filter.indexOf(SPACE)+1, findClosingParentheses(filter))),NOT_OPERATOR);
				}
			}
			
			// We want to find the logical operator that is associated with the current filter expression
			int startOfOperator = findIndexOfNextLogicalOperator(removeParentheses(filter));
			
			// The end of the operator should be located one index below the space following the actual operator
			int endOfOperator = filter.indexOf(SPACE, startOfOperator)-1;
			
			//If no index for an expected logical operator was found throw an exception
			if(startOfOperator<0) {
				throw new InvalidFilterExpression();
			}
			
			// The end of the left block is 2 indexes below the operator as space is between them
			int endOfLeftBlock = startOfOperator-2;
			
			// The start of the right block is 2 indexes after the end of the operator 
			int startOfRightBlock = endOfOperator+2;
			
			//Detects if an operator block is valid
			if(startOfOperator>endOfOperator) { 
				throw new InvalidFilterExpression();
			}
			
			//Detects if a left block of a compound filter is valid
			if(endOfLeftBlock<1) {
				throw new InvalidFilterExpression();
			}
			
			//Detects if a right block of a compound filter is valid 
			if(startOfRightBlock>filter.length()-1) {
				throw new InvalidFilterExpression();
			}
			
			/*
			 * Build blocks to pass to the recursive method in order to create the next node
			 */
			String leftBlock = filter.substring(FIRST_INDEX_OF_STRING+1, endOfLeftBlock+1);
			String rightBlock = filter.substring(startOfRightBlock, filter.length()-1);
			String operator = filter.substring(startOfOperator,  endOfOperator+1);
			
			return new ExpressionTreeNode(parseExpression(node,leftBlock), parseExpression(node,rightBlock),operator);
		}
	}
	
	
	
	/**
	 * Detects the filter type
	 * @param filter
	 * @return 1 if the filter is a basic filter, 2 if it is a compound filter
	 */
	private static int detectFilterType(String filter) {
		if(filter.contains(LEFT_PARENTHESES)) {
			return COMPOUND_MARKER;
		} else {
			return BASIC_MARKER;
		}
	}
	
	private static String removeParentheses(String string) {
		return string.substring(1, string.length()-1);
	}
	
	
	private static int findClosingParentheses(String string) throws InvalidFilterExpression {
		int parenthesesCounter = 0; 
		for(int i=0;i<string.length();i++) {
			if(string.substring(i, i+1).equals(LEFT_PARENTHESES)) {
				parenthesesCounter++;
			}
			if(string.substring(i, i+1).equals(RIGHT_PARENTHESES)) {
				parenthesesCounter--;
			}
			if(parenthesesCounter==0 && i!=0) {
				return i;
			}
		}
		throw new InvalidFilterExpression();
	}
	
	private static int findIndexOfNextLogicalOperator(String string) throws InvalidFilterExpression {
		/*If there is a logical operator, it is a compound filter
		 * so a left parentheses was already detected. We will start the count at 0
		 * skipping the first char
		 */
		int index = findClosingParentheses(string);	
		if(index==string.length()-1) {
			return string.indexOf(SPACE, 0);
		} else {
			return string.indexOf(SPACE, index)+2;
		}		
		
	}
}
